package com.study.bean;

import com.study.model.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Objects;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Component //属性value指定bean的id，如果不指定，默认是当前类的类名，首字母小写
@Scope("singleton")
public class UserServiceImpl implements UserService {

    @Value("张三")
    private String name;
    @Value("${age}")
    private Integer age;

    @Autowired(required = false)
    DataSource dataSource;

    @Autowired(required = false)
    DataSourceTransactionManager transactionManager;

    @Autowired(required = false)
    TransactionTemplate transactionTemplate;

    /**
     * @Autowired：默认按照类型注入，默认情况下要求依赖对象必须存在，如果允许null值，设置require属性为false。Spring自带
     * @Qualifier：在自动按照类型注入的基础上，再按照bean的id注入；在给字段注入时不能独立使用，必须结合@Autowired一起使用；但是给方法参数注入时，可以独立使用。
     * @Resource：默认按照名称注入，如果找不到且没有指定name属性，这时才会根据类型注入，一旦name属性指定了，只会根据名称注入！Jdk自带
     * @Inject：默认按照类型自动装配，如果需要根据名称装配需要配合@Named使用。JSR330中的规范，需要额外导包。
     * @Value：给基本类型、string类型注入值，可以使用占位符获取属性文件中的值。
     */
    @Autowired
    private UserDao userDao;

    public UserServiceImpl(String name, Integer age, UserDao userDao) {
        this.name = name;
        this.age = age;
        this.userDao = userDao;
    }

    @PostConstruct //相当于init-method
    public void init() {
        System.out.println("UserService初始化方法");
    }

    @PreDestroy //相当于destroy-method
    public void destroy() {
        System.out.println("UserService销毁方法");
    }


    @Override
    public void save(User user) {
        if (Objects.isNull(user)) {
            throw new NullPointerException();
        }
        System.out.println("保存用户成功");
    }

    @Override
    public void testProgrammingTransaction() throws Exception {
        //最原始编程式事务
        Connection connection = dataSource.getConnection();
        try {
            //设置不自动提交
            connection.setAutoCommit(false);
            PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO t_user VALUES('1','张三','18')");
            preparedStatement.execute();
            int i = 1 / 0;
            connection.commit();
        } catch (Exception ex) {
            ex.printStackTrace();
            connection.rollback();
        }
        //使用TransactionManager的编程式事务
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transactionStatus = transactionManager.getTransaction(def);
        try {
            userDao.addUser();
            // int i = 1 / 0;
            transactionManager.commit(transactionStatus);
        } catch (Exception ex) {
            transactionManager.rollback(transactionStatus);
        }
        //使用TransactionTemplate进一步简化后的编程式事务
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                userDao.addUser();
                //int i = 1 / 0;
            }
        });
    }

    @Override
    public void testXmlDeclareTransaction() {
        userDao.addUser();
        int i = 1 / 0;
    }

    @Override
    @Transactional
    public void testAnnotationDeclareTransaction() {
        userDao.addUser();
        int i = 1 / 0;
    }


}
